﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PPSkin
{
    public partial class PPHScrollBarExt : UserControl
    {
        #region 私有参数
        private int value = 0;
        private int maximum = 100;
        private int minimum = 0;
        private int visibleValue = 10;
        private int smallChange = 1;
        private int largeChange = 10;

        private int baseSize = 2;
        private int baseRadius = 0;
        private int buttonSize = 10;
        private int buttonRadius = 10;

        private Color buttonColor = Color.DodgerBlue;
        private Color buttonHoverColor = Color.DeepSkyBlue;
        private Color baseColor = Color.Gainsboro;



        protected bool isMouseHover = false;//表示鼠标是否移入滚动按钮
        protected bool isMouseDown = false;//表示鼠标是否在滚动按钮按下
        protected int mouseDownPoint = 0;//记录鼠标在滚动按钮按下时鼠标距离按钮左边缘的距离

        private Control bindControl = null;
        public Control BindControl
        {
            get { return bindControl; }
        }

        #endregion

        #region 公共参数
        [Description("滚动条值"), Category("PPSkin")]
        public int Value
        {
            get { return this.value; }
            set
            {
                if (value > maximum-visibleValue)
                {
                    this.value = maximum - visibleValue;
                }
                else if (value < minimum)
                {
                    this.value = minimum;
                }
                else
                {
                    this.value = value;
                }
                this.Invalidate();

            }
        }

        [Description("滚动条最大值"), Category("PPSkin")]
        public int Maximum
        {
            get { return maximum; }
            set
            {
                if (value < minimum)
                    return;
                this.maximum = value;
                if (this.value > maximum)
                {
                    this.value = maximum;
                }
                //visibleValue =(int)((this.Height - 1) * ((float)largeChange / (maximun - minimun)));
                this.Invalidate();
            }
        }

        [Description("滚动条最小值"), Category("PPSkin")]
        public int Minimum
        {
            get { return this.minimum; }
            set
            {
                if (value > maximum)
                    return;
                this.minimum = value;
                if (this.value < minimum)
                {
                    this.value = minimum;
                }
                this.Invalidate();
            }
        }

        [Description("单击箭头的变化幅度"), Category("PPSkin")]
        public int SmallChange
        {
            get { return smallChange; }
            set
            {
                if (value < 0 || value > (maximum - minimum))
                    return;
                this.smallChange = value;
                this.Invalidate();
            }
        }

        [Description("单击滚动条的变化幅度"), Category("PPSkin")]
        public int LargeChange
        {
            get { return largeChange; }
            set
            {
                if (value < 0 || value > (maximum - minimum))
                    return;
                this.largeChange = value;
                this.Invalidate();
            }
        }

        [Description("绑定控件当前能显示的高度"), Category("PPSkin")]
        public int VisibleValue
        {
            get { return visibleValue; }
            set
            {
                
                this.visibleValue = value;
                this.Invalidate();
            }
        }

        [Description("背景的宽度"), Category("PPSkin")]
        public int BaseSize
        {
            get { return baseSize; }
            set
            {

                this.baseSize = value;
                this.Invalidate();
            }
        }

        [Description("背景的圆角"), Category("PPSkin")]
        public int BaseRadius
        {
            get { return baseRadius; }
            set
            {

                this.baseRadius = value;
                this.Invalidate();
            }
        }

        [Description("滚动按钮的宽度"), Category("PPSkin")]
        public int ButtonSize
        {
            get { return buttonSize; }
            set
            {

                this.buttonSize = value;
                this.Invalidate();
            }
        }

        [Description("滚动按钮的圆角"), Category("PPSkin")]
        public int ButtonRadius
        {
            get { return buttonRadius; }
            set
            {

                this.buttonRadius = value;
                this.Invalidate();
            }
        }


        [Description("滚动按钮颜色"), Category("PPSkin")]
        public Color ButtonColor
        {
            get { return buttonColor; }
            set
            {
                buttonColor = value;
                this.Invalidate();
            }
        }

        [Description("滚动按钮鼠标移入的颜色"), Category("PPSkin")]
        public Color ButtonHoverColor
        {
            get { return buttonHoverColor; }
            set
            {
                buttonHoverColor = value;
                this.Invalidate();
            }
        }

        [Description("背景颜色"), Category("PPSkin")]
        public Color BaseColor
        {
            get { return baseColor; }
            set
            {
                baseColor = value;
                this.Invalidate();
            }
        }
        #endregion

        #region 事件
        public event EventHandler ValueChanged;
        #endregion

        public PPHScrollBarExt()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.Selectable, true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.UpdateStyles();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;

            SolidBrush baseBrush = new SolidBrush(baseColor);
            SolidBrush buttonBrush = new SolidBrush(buttonColor);
            SolidBrush buttonHoverBrush = new SolidBrush(buttonHoverColor);


            GraphicsPath basepath = PaintHelper.CreatePath(new RectangleF( 0, (float)(this.Height - baseSize) / 2, this.Width-1, baseSize), baseRadius);
            g.FillPath(baseBrush, basepath);//画背景


            RectangleF buttonRect = GetButtonRect();
            GraphicsPath buttonPath = PaintHelper.CreatePath(buttonRect, buttonRadius);


            if(isMouseHover)//判断鼠标是否移入按钮
            {
                g.FillPath(buttonHoverBrush, buttonPath);
            }
            else
            {
                g.FillPath(buttonBrush, buttonPath);
            }

            basepath.Dispose();
            buttonPath.Dispose();
            baseBrush.Dispose();
            buttonBrush.Dispose();
            buttonHoverBrush.Dispose();
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if(!isMouseDown)//鼠标未按下
            {
                if (e.Delta > 0)
                {
                    this.Value -= smallChange;
                }
                else
                {
                    this.Value += smallChange;
                }
                this.Invalidate();
                if (ValueChanged != null)
                {
                    this.BeginInvoke(new Action(() => { ValueChanged(this, new EventArgs()); }));
                }
            }

        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if(e.Button==MouseButtons.Left)
            {
                RectangleF buttonRect = GetButtonRect();
                Point mousePoint = e.Location;//获取鼠标相对于控件的位置

                if (buttonRect.Contains(mousePoint))
                {
                    isMouseDown = true;
                    mouseDownPoint = (int)(mousePoint.X - buttonRect.Left);
                }
                else
                {
                    float buttonWidth = (float)((this.Width - 1) * ((float)visibleValue / (maximum - minimum)));
                    float x = mousePoint.X - buttonWidth / 2;
                    this.Value = (int)((maximum - minimum-visibleValue) * x / (this.Width - buttonWidth));
                    if (ValueChanged != null)
                    {
                        this.BeginInvoke(new Action(() => { ValueChanged(this, new EventArgs()); }));
                    }
                    this.Invalidate();
                }
            }

        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            RectangleF buttonRect = GetButtonRect();
            Point mousePoint = e.Location;//获取鼠标相对于控件的位置
            bool ishover = isMouseHover;
            if (!isMouseDown)
            {
                if (buttonRect.Contains(mousePoint))
                {
                    this.Cursor = Cursors.Hand;
                    isMouseHover = true;
                }
                else
                {
                    this.Cursor = Cursors.Default;
                    isMouseHover = false;
                }
            }
            if (!(ishover && isMouseHover))
                this.Invalidate();

            if(isMouseDown)
            {
                
                int currentX = mousePoint.X;

                int newLeft = currentX - mouseDownPoint;


                 
                this.Value =(int)( (maximum - minimum-visibleValue) * (newLeft) / (this.Width - 1 - buttonRect.Width));
                //Console.WriteLine("downY:{0},currentY:{1},delta:{2},value:{3}", downY, currentY, delta, Value);
                this.Invalidate();
                if (ValueChanged != null)
                {
                    this.BeginInvoke(new Action(() => { ValueChanged(this, new EventArgs()); }));
                }
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            isMouseDown = false;
            RectangleF buttonRect = GetButtonRect();
            Point mousePoint = e.Location;//获取鼠标相对于控件的位置
            bool ishover = isMouseHover;
            if (buttonRect.Contains(mousePoint))
            {
                this.Cursor = Cursors.Hand;
                isMouseHover = true;
            }
            else
            {
                this.Cursor = Cursors.Default;
                isMouseHover = false;
            }
            if (!(ishover && isMouseHover))
                this.Invalidate();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            if(!isMouseDown)
            {
                isMouseHover = false;
            }
            this.Cursor = Cursors.Default;
            this.Invalidate();
        }

        private RectangleF GetButtonRect()
        {
            float buttonWidth= (float)((this.Width - 1) * ((float)visibleValue / (maximum - minimum)));
            if (buttonWidth < 10)
                buttonWidth = 10;
            RectangleF buttonRect = new RectangleF((float)((this.Width - 1 - buttonWidth) * ((float)value / (maximum - minimum-visibleValue))), (this.Height - buttonSize) / 2, buttonWidth,buttonSize);//获取按钮区域
            return buttonRect;
        }

        /// <summary>
        /// 将滚动条绑定控件
        /// </summary>
        /// <param name="control"></param>
        public void BindingControl(Control control)
        {
            if (control == null || control.IsDisposed||bindControl!=null)
                return;
            bindControl = control;
            this.ValueChanged += PPHScrollBarExt_ValueChanged;
            bindControl.Disposed += BindControl_Disposed;
            bindControl.SizeChanged += BindControl_SizeChanged;

            if(bindControl is RichTextBox)
            {
                (bindControl as RichTextBox).TextChanged += BindControl_TextChanged;
                (bindControl as RichTextBox).SelectionChanged += BindControl_SelectionChanged;
            }
            else if(bindControl is DataGridView)
            {
                (bindControl as DataGridView).ColumnWidthChanged += BindControl_ColumnWidthChanged;
            }

            SetScrollBarNum();
        }


        private void BindControl_SizeChanged(object sender, EventArgs e)
        {
            if (bindControl == null || bindControl.IsDisposed)
                return;
            SetScrollBarNum();
        }

        private void BindControl_Disposed(object sender, EventArgs e)
        {
            bindControl.Disposed -= BindControl_Disposed;
            bindControl.SizeChanged -= BindControl_SizeChanged;
            if (bindControl is RichTextBox)
            {
                (bindControl as RichTextBox).TextChanged -= BindControl_TextChanged;
                (bindControl as RichTextBox).SelectionChanged -= BindControl_SelectionChanged;
            }
            else if (bindControl is DataGridView)
            {
                (bindControl as DataGridView).ColumnWidthChanged -= BindControl_ColumnWidthChanged;
            }

            bindControl = null;
        }

        private void BindControl_TextChanged(object sender,EventArgs e)
        {
            if (bindControl == null || bindControl.IsDisposed)
                return;
            SetScrollBarNum();
        }

        private void BindControl_SelectionChanged(object sender,EventArgs e)
        {
            if (bindControl == null || bindControl.IsDisposed)
                return;
            SetScrollBarNum();
        }

        private void BindControl_ColumnWidthChanged(object sender,DataGridViewColumnEventArgs e)
        {
            ResetDataGridViewScrollBarNum();
        }

        private void PPHScrollBarExt_ValueChanged(object sender, EventArgs e)
        {
            if (bindControl == null || bindControl.IsDisposed)
                return;
            SetControlNum();
        }

        private void SetScrollBarNum()
        {
            if (bindControl == null || bindControl.IsDisposed)
                return;
            if (bindControl is DataGridView)
            {
                ResetDataGridViewScrollBarNum();
            }
            else
            { 
                Win32.Scrollinfo si = new Win32.Scrollinfo();
                si.cbSize = (uint)Marshal.SizeOf(si);
                si.fMask = (int)(Win32.ScrollInfoMask.SIF_DISABLENOSCROLL | Win32.ScrollInfoMask.SIF_ALL);
                Win32.GetScrollInfo(bindControl.Handle, (int)Win32.ScrollBarDirection.SB_HORZ, ref si);
               
                this.visibleValue = (int)si.nPage-1;
                this.Maximum = si.nMax;
                this.Minimum = si.nMin;
                this.Value = si.nPos;
                if(si.nPage==0)
                {
                    this.Visible = false;
                }
                else
                {
                    this.Visible = true;
                }
               // Console.WriteLine(string.Format("nPage:{0};nMax:{1};nMin:{2};nPose:{3};Max:{4};Min:{5};VisValue:{6};value:{7}", si.nPage, si.nMax, si.nMin, si.nPos,Maximum,Minimum,visibleValue,value));
            }
        }

        private void SetControlNum()
        {
            if (bindControl == null || bindControl.IsDisposed)
                return;
            if (bindControl is DataGridView)
            {
                DataGridView d = (bindControl as DataGridView);
                if (d.Rows.Count > 0)
                    d.HorizontalScrollingOffset = this.Value;
            }
            else
            {
                Win32.Scrollinfo si = new Win32.Scrollinfo();
                si.cbSize = (uint)Marshal.SizeOf(si);
                si.fMask = (int)(Win32.ScrollInfoMask.SIF_DISABLENOSCROLL | Win32.ScrollInfoMask.SIF_ALL);
                Win32.GetScrollInfo(bindControl.Handle, (int)Win32.ScrollBarDirection.SB_HORZ, ref si);
                si.nPos = this.Value;
                Win32.SetScrollInfo(bindControl.Handle, (int)Win32.ScrollBarDirection.SB_HORZ, ref si, true);
                IntPtr aa = new IntPtr(Win32.MakeLong((short)Win32.SB_THUMBTRACK, (short)(si.nPos)));
                Win32.SendMessage(bindControl.Handle, Win32.WM_HSCROLL, aa, new IntPtr());
            }
                
        }

        private void ResetDataGridViewScrollBarNum()
        {
            DataGridView d = (bindControl as DataGridView);
            int totalValue = 0;
            if (d.RowHeadersVisible)
            {
                totalValue += d.RowHeadersWidth;
            }
            if (d.Columns.Count > 0)
            {
                for (int i = 0; i < d.Columns.Count; i++)
                {
                    totalValue += d.Columns[i].Width;
                }
                this.Maximum = totalValue;
                if(d.Width>this.Maximum)
                {
                    this.visibleValue = 0;
                }
                else
                {
                    visibleValue = d.Width;
                }
            }
            else
            {
                this.visibleValue = 0;
            }

            if (this.visibleValue == 0)
            {
                this.Visible = false;
            }
            else
            {
                this.Visible = true;
            }
        }
    }
}
